package main

import (
	"bytes"
	"fmt"
	"strconv"
	"strings"
	"time"
)

/*字符串:
一个字符串是一个不可改变的字节序列，字符串通常是用来包含人类可读的文本数据。和数组不同的是，字符串的元素不可修改，
是一个只读的字节数组。每个字符串的长度虽然也是固定的，但是字符串的长度并不是字符串类型的一部分。由于Go语言的源代码要求是UTF8编码，
导致Go源代码中出现的字符串面值常量一般也是UTF8编码的。源代码中的文本字符串通常被解释为采用UTF8编码的Unicode码点（rune）序列。
因为字节序列对应的是只读的字节序列，因此字符串可以包含任意的数据，包括byte值0。我们也可以用字符串表示GBK等非UTF8编码的数据，
不过这种时候将字符串看作是一个只读的二进制数组更准确，因为for range等语法并不能支持非UTF8编码的字符串的遍历。

Go语言字符串的底层结构在reflect.StringHeader中定义：
type StringHeader struct {
    Data uintptr
    Len  int
}
字符串结构由两个信息组成：第一个是字符串指向的底层字节数组，第二个是字符串的字节的长度。字符串其实是一个结构体，
因此字符串的赋值操作也就是reflect.StringHeader结构体的复制过程，并不会涉及底层字节数组的复制。
在前面数组一节提到的[2]string字符串数组对应的底层结构和[2]reflect.StringHeader对应的底层结构是一样的，
可以将字符串数组看作一个结构体数组。

字符串虽然不是切片，但是支持切片操作，不同位置的切片底层也访问的同一块内存数据（因为字符串是只读的，相同的字符串面值常量通常是对应同一个字符串常量）：
*/

/**
字符串可以通过切片访问
*/
func string1() {
	s := "hello, world,中文"
	hello := s[:5]
	world := s[7:]
	fmt.Println(hello, world)
	fmt.Println(len(s), len([]rune(s))) //字节长度，字符长度
}

/**
字节序列
*/
func string2() {
	fmt.Printf("%#v\n", []byte("Hello, 世界"))
	//[]byte{0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x2c, 0x20, 0xe4, 0xb8, 0x96, 0xe7, 0x95, 0x8c}
	//												   0xe4, 0xb8, 0x96对应中文“世”，0xe7, 0x95, 0x8c对应中文“界”
	fmt.Println("\xe4\xb8\x96") // 打印: 世
	fmt.Println("\xe7\x95\x8c") // 打印: 界

	//Go语言的字符串中可以存放任意的二进制字节序列，而且即使是UTF8字符序列也可能会遇到坏的编码。
	//如果遇到一个错误的UTF8编码输入，将生成一个特别的Unicode字符‘\uFFFD’，这个字符在不同的软件中的显示效果可能不太一样，
	//在印刷中这个符号通常是一个黑色六角形或钻石形状，里面包含一个白色的问号‘�’。

	//下面的字符串中，我们故意损坏了第一字符的第二和第三字节，因此第一字符将会打印为“�”，第二和第三字节则被忽略，
	//后面的“abc”依然可以正常解码打印（错误编码不会向后扩散是UTF8编码的优秀特性之一）。
	fmt.Println("\xe4\x00\x00\xe7\x95\x8cabc") // �界abc
}

/**
遍历
*/
func string3() {
	s := "hello world 中文"
	for i, c := range []rune(s) {
		fmt.Printf("%d,%c\n", i, c)
	}
}

/**
字符串 数字互转
*/
func string4() {
	//字符串转int
	x, err := strconv.Atoi("1002")
	if err != nil {
		panic(err)
	}
	fmt.Println(x)

	//字符串转float
	f, _ := strconv.ParseFloat("10.234", 10)
	fmt.Println(f)

	//数字转字符串
	s := strconv.Itoa(234)
	fmt.Println(s)

}

/**
字符串常用操作
*/
func string5() {
	//字符串比较
	com01 := strings.Compare("hello", "hello")
	if com01 == 0 {
		fmt.Println("相等")
	} else {
		fmt.Println("不相等")
	}
	//包含
	var isCon bool = strings.Contains("hello world", "hello")
	fmt.Println(isCon) //true

	lastIndex := strings.LastIndex("hello world", "o")
	fmt.Println("在字符串中最后出现位置的索引 " + strconv.Itoa(lastIndex)) //7
	//-1 表示字符串 s 不包含字符串

	//统计给定子串sep的出现次数, sep为空时, 返回1 + 字符串的长度
	fmt.Println(strings.Count("cheeseeee", "ee")) // 3
	fmt.Println(strings.Count("five", ""))        // 5

	// 重复s字符串count次, 最后返回新生成的重复的字符串
	fmt.Println("hello " + strings.Repeat("world ", 10))

	//替换
	// 在s字符串中, 把old字符串替换为new字符串，n表示替换的次数，小于0表示全部替换
	var str03 string = "/Users//Documents/GOPatch/src/MyGO/config/TestString/"
	fmt.Println(strings.Replace(str03, "/", "**", -1)) //n:替换个数，-1表示所有
	fmt.Println(strings.Replace(str03, "/", "**", 4))

	//删除字符串的开头和尾部
	fmt.Println("删除两头的/ = " + strings.Trim(str03, "/"))
	fmt.Println("删除左边的/ =  " + strings.TrimLeft(str03, "/"))

	str06 := strings.TrimSpace(" hello hao hao hao ")
	fmt.Println("删除开头末尾的空格 =" + str06)

	// 大小写
	str07 := "hello hao hao hao"
	fmt.Println(strings.Title(str07))                  //Hello Hao Hao Hao
	fmt.Println(strings.ToLower(" Hello Hao Hao Hao")) // hello hao hao hao
	fmt.Println(strings.ToUpper(str07))                //HELLO HAO HAO HAO

	//前缀 后缀
	fmt.Println(strings.HasPrefix("Gopher", "Go")) // true
	fmt.Println(strings.HasSuffix("Amigo", "go"))  // true

	//字符串分割
	fieldsStr := "  hello   it's  a  nice day today    "
	//根据空白符分割,不限定中间间隔几个空白符
	fieldsSlece := strings.Fields(fieldsStr)
	fmt.Println(fieldsSlece) //[hello it's a nice day today]

	//根据特定字符分割
	slice01 := strings.Split("q,w,e,r,t,y,", ",")
	fmt.Println(slice01)      //[q w e r t y ]
	fmt.Println(cap(slice01)) //7  最后多个空""

	//拼接
	//Join 用于将元素类型为 string 的 slice, 使用分割符号来拼接组成一个字符串：
	var str08 string = strings.Join(fieldsSlece, ",")
	fmt.Println("Join拼接结果=" + str08) //hello,it's,a,nice,day,today
}

/**
 * 反转字符串，输入Douyu，输出uyuoD
 * @param str string字符串 任意字符串
 * @return string字符串
 */

func reverse(str string) string {
	// write code here
	r := []rune(str)
	for i, j := 0, len(r)-1; i < j; i, j = i+1, j-1 {
		r[i], r[j] = r[j], r[i]
	}
	return string(r)
}

/**
字符串拼接效率
*/
func string6() {
	fmt.Println("------------对比字符串拼接效率----------------")
	var buffer bytes.Buffer

	start := time.Now()
	for i := 0; i < 100000; i++ {
		buffer.WriteString("test is here\n")
	}
	_ = buffer.String() // 拼接结果
	end := time.Now()
	fmt.Println("Buffer time is ", end.Sub(start).Seconds())

	start = time.Now()
	str := ""
	for i := 0; i < 100000; i++ {
		str += "test is here\n"
	}
	end = time.Now()
	fmt.Println("+= time is ", end.Sub(start).Seconds())

	start = time.Now()
	var sl []string
	for i := 0; i < 100000; i++ {
		sl = append(sl, "test is here\n")
	}
	strings.Join(sl, "")
	end = time.Now()
	fmt.Println("Join time is", end.Sub(start).Seconds())
	/*
	   Buffer time is  0.0037985
	   += time is  8.0608893
	   Join time is 0.0078091
	*/

}

func main52() {
	//string1()
	//string2()
	//string3()
	//string4()
	//string5()
	string6()
}
